package org.visionlibrary.image.geomtric.util;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.geomtric.model.Acumulator2d;
import org.visionlibrary.image.geomtric.model.Function1d;
import org.visionlibrary.image.geomtric.model.Mask2d;
import org.visionlibrary.image.geomtric.model.Vector2d;


/**
 * Klasa narzedziowa zawierajaca ogolne metody pracy z plikami, czasem, wydrukami
 * itp. Podzielona jest na cztery kategorie.
 * 
 */
public class Debug {
	public static class Primitives {
		public static void printArray(int[] array) {
			System.out.println(java.util.Arrays.toString(array));
		}

		public static void printArray(float[] array) {
			System.out.println(java.util.Arrays.toString(array));
		}

		public static void printArray(int[][] array) {
			for (int i = 0; i < array.length; i++)
				printArray(array[i]);
		}

		public static void printArray(float[][] array) {
			for (int i = 0; i < array.length; i++)
				printArray(array[i]);
		}

		public static void print(int[] array, OutputStream os,
				boolean removeZero) throws IOException {
			os.write(new String("Array length = " + array.length + "\r\n")
					.getBytes());
			for (int i = 0; i < array.length; i++) {
				int tmp = array[i];
				if (removeZero && tmp == 0)
					continue;

				os.write(new String((i + 1) + " " + tmp + "\r\n").getBytes());
			}
		}

		public static void print(int[] array, boolean removeZero)
				throws IOException {
			print(array, System.out, removeZero);
		}

		public static void print(double[] array) throws IOException {
			print(array, System.out);
		}

		public static void print(double[] array, OutputStream os)
				throws IOException {
			os.write(new String("Array length = " + array.length + "\r\n")
					.getBytes());
			for (int i = 0; i < array.length; i++) {
				os.write(new String(i + " " + array[i] + "\r\n").getBytes());
			}
		}
	}

	public static class Misc {
		public static String getClassName(Class<?> c) {
			String fqClassName = c.getName();
			int firstChar;
			firstChar = fqClassName.lastIndexOf('.') + 1;
			if (firstChar > 0) {
				fqClassName = fqClassName.substring(firstChar);
			}
			return fqClassName;
		}

		public static String getFullClassName(Class<?> c) {
			return c.getName();
		}

		public static String getPackageName(Class<?> c) {
			String fullyQualifiedName = c.getName();
			int lastDot = fullyQualifiedName.lastIndexOf('.');
			if (lastDot == -1) {
				return "";
			}
			return fullyQualifiedName.substring(0, lastDot);
		}

		public static String getFileNameWithoutExt(String filepath) {
			String filename = (new File(filepath)).getName();
			filename = filename.substring(0, filename.lastIndexOf("."));
			return filename;
		}

		public static String getTimeStamp() {
			return (new SimpleDateFormat("hh_mm_ss__dd_MM_yyyy"))
					.format(Calendar.getInstance().getTime());
		}

		public static Color getRandomColor() {
			return Color.getHSBColor((new Random()).nextFloat(), 1.0f, 1.0f);
		}

		public static TiledImage drawAcumulator2d(Acumulator2d acumulator,
				TiledImage src) {
			if (null == acumulator)
				throw new NullPointerException("Acumulator2d is null");

			ImageFactory imf = new ImageFactory();
			TiledImage result = imf.createCompatibleGrayScaleImage(src,
					acumulator.width(), acumulator.height());

			for (int i = 0; i < acumulator.width(); i++) {
				for (int j = 0; j < acumulator.height(); j++) {
					for (int b = 0; b < result.getNumBands(); b++) {
						result.setSample(i, j, b, acumulator.get(i, j));
					}
				}
			}

			return result;
		}

		public static TiledImage drawMask(Mask2d mask, TiledImage src) {
			TiledImage result = (new ImageFactory()).createCompatibleImage(src,
					mask.width(), mask.height());

			for (int i = 0; i < mask.width(); i++)
				for (int j = 0; j < mask.height(); j++) {
					int color = 0;
					if (mask.get(i, j)) {
						color = 0;
					} else {
						color = 255;
					}

					for (int ch = 0; ch < src.getNumBands(); ch++)
						result.setSample(i, j, ch, color);
				}

			return result;
		}
	}

	public static class Timer {
		private static long tstart = 0;
		private static long tstop = 0;

		public static void startTimer(String message, OutputStream os)
				throws IOException {
			os.write(message.getBytes());
			startTimer();
		}

		public static void startTimer(String message) throws IOException {
			startTimer(message, System.out);
		}

		public static void startTimer() {
			tstart = (new GregorianCalendar()).getTimeInMillis();
		}

		public static long stopTimer() {
			tstop = (new GregorianCalendar()).getTimeInMillis();
			return tstop - tstart;
		}
	}

	public static class Vectors {
		public static void print(List<Vector2d> list, OutputStream os)
				throws IOException {
			if (null == list)
				os.write("Debug: print(List<Vector2d>) - List is null"
						.getBytes());
			else if (list.size() == 0)
				os.write("Debug: print(List<Vector2d>) - List is empty"
						.getBytes());
			else {
				os.write(new String(
						"Debug: print(List<Vector2d>) - List size = "
								+ list.size() + "\r\n").getBytes());
				for (int i = 0; i < list.size(); i++) {
					print(list.get(i), os);
				}
			}
		}

		public static void print(List<Vector2d> list) throws IOException {
			print(list, System.out);
		}

		public static void print(Vector2d vector, OutputStream os)
				throws IOException {
			os.write(new String(vector + "\r\n").getBytes());
		}
	}

	public static class Functions {
		public static void print(List<? extends Function1d> list,
				OutputStream os) throws IOException {
			if (null == list)
				os.write("Debug: print(List<Function1d>) - List is null\r\n"
						.getBytes());
			else if (list.size() == 0)
				os.write("Debug: print(List<Function1d>) - List is empty\r\n"
						.getBytes());
			else {
				os.write(new String(
						"Debug: print(List<Function1d>) - List size = "
								+ list.size() + "\r\n").getBytes());
				for (int i = 0; i < list.size(); i++) {
					print(list.get(i), os);
				}
			}
		}

		public static void print(List<? extends Function1d> list)
				throws IOException {
			print(list, System.out);
		}

		public static void print(Function1d function, OutputStream os)
				throws IOException {
			os.write(new String(function.toString() + "\r\n").getBytes());
		}

		public static void test(Function1d func, int begin, int end,
				OutputStream os) throws IOException {
			os.write(new String(func.toString() + "\r\n").getBytes());
			for (int x = begin; x <= end; x++) {
				double y = func.evaluateY(x);
				os.write(new String("f(" + x + ")=" + y + "\r\n").getBytes());
			}
		}

		public static void test(Function1d func, int end, OutputStream os)
				throws IOException {
			test(func, 0, end, os);
		}

		public static void test(Function1d func, int end) throws IOException {
			test(func, end, System.out);
		}

		public static void test(Function1d func, int begin, int end)
				throws IOException {
			test(func, begin, end, System.out);
		}
	}

	public static class Image {
		private static final File DEFAULT_PATH = new File("debug");
		private static boolean enabled = true;
		private static boolean shortClass = true;
		private static boolean descFilename = true;
		private static boolean show = false;
		private static boolean save = true;
		private static ThreadLocal<File> debugFolder = new ThreadLocal<File>();

		public static void info(BufferedImage image,
				List<Pair<Vector2d, Color>> colorlines, String desc, int width,
				int height) {
			info(getImage(image, colorlines, desc, width, height), desc);
		}

		public static void info(BufferedImage image, List<Vector2d> lines,
				Vector2d line1, Vector2d line2, Vector2d line3, String desc,
				int width, int height) {
			BufferedImage img = image;
			img = getImage(img, lines, desc, width, height, Color.BLACK);
			if (null != line1)
				img = getImage(img, line1, desc, width, height, Color.BLUE);

			if (null != line2)
				img = getImage(img, line2, desc, width, height, Color.BLUE);

			if (null != line3)
				img = getImage(img, line3, desc, width, height, Color.RED);

			info(img, desc);
		}

		public static BufferedImage getImage(BufferedImage image,
				List<Pair<Vector2d, Color>> colorlines, String desc, int width,
				int height) {
			BufferedImage img = image;
			if (null == image)
				img = new BufferedImage(width, height,
						BufferedImage.TYPE_INT_RGB);

			Graphics2D g2d = img.createGraphics();
			if (null == image) {
				g2d.setBackground(Color.WHITE);
				g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
			}
			for (int i = 0; i < colorlines.size(); i++) {
				Vector2d line = colorlines.get(i).getLeft();
				g2d.setColor(colorlines.get(i).getRight());
				if (null == colorlines.get(i).getRight())
					g2d.setColor(Debug.Misc.getRandomColor());
				g2d.drawLine(line.getFirst().getXAsInt(), line.getFirst()
						.getYAsInt(), line.getLast().getXAsInt(), line
						.getLast().getYAsInt());
			}
			return img;
		}

		public static BufferedImage getImage(BufferedImage image,
				Vector2d line, String desc, int width, int height, Color color) {
			BufferedImage img = image;
			if (null == image)
				img = new BufferedImage(width, height,
						BufferedImage.TYPE_INT_RGB);

			Graphics2D g2d = img.createGraphics();
			if (null == image) {
				g2d.setBackground(Color.WHITE);
				g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
			}
			if (null == color)
				color = Debug.Misc.getRandomColor();
			g2d.setColor(color);
			g2d.drawLine(line.getFirst().getXAsInt(), line.getFirst()
					.getYAsInt(), line.getLast().getXAsInt(), line.getLast()
					.getYAsInt());

			return img;
		}

		public static void info(BufferedImage image, List<Vector2d> lines,
				String desc, int width, int height, Color color) {
			info(getImage(image, lines, desc, width, height, color), desc);
		}

		public static BufferedImage getImage(BufferedImage image,
				List<Vector2d> lines, String desc, int width, int height,
				Color color) {
			BufferedImage img = image;
			boolean randomColor = false;
			if (null == image)
				img = new BufferedImage(width, height,
						BufferedImage.TYPE_INT_RGB);

			Graphics2D g2d = img.createGraphics();
			if (null == image) {
				g2d.setBackground(Color.WHITE);
				g2d.clearRect(0, 0, width, height);
			}
			if (null == color)
				randomColor = true;
			for (int i = 0; i < lines.size(); i++) {
				Vector2d line = lines.get(i);
				if (randomColor)
					color = Debug.Misc.getRandomColor();
				g2d.setColor(color);
				g2d.drawLine(line.getFirst().getXAsInt(), line.getFirst()
						.getYAsInt(), line.getLast().getXAsInt(), line
						.getLast().getYAsInt());
			}

			return img;
		}

		public static void info(BufferedImage image, String desc) {
			if (image == null) {
				System.out.println("WARN: image is null");
			}
			if (!isEnabled()) {
				return;
			}
			String filename = desc;
			if (!descFilename || desc == null) {
				filename = getMethodName();
			}

			if (save) {
				if (!getDebugFolder().exists()) {
					getDebugFolder().mkdirs();
				}
				String path = getDebugFolder() + "/" + filename + ".bmp";
				try {
					ImageIO.write(image, "png", new File(path));
					System.out.println("INFO: Image saved: " + path);
				} catch (IOException e) {
					System.out.println("INFO: Cannot save image: " + path);
					e.printStackTrace(System.out);
				}
			}
			if (show) {
				TestUtils.showImage(image, filename, false);
			}
		}

		private static File getDebugFolder() {
			File file = debugFolder.get();
			if (file == null) {
				return DEFAULT_PATH;
			}
			return file;
		}

		public static synchronized void info(TiledImage image, String desc) {
			if (image == null) {
				System.out.println("WARN: image is null");
			}
			info(image.getAsBufferedImage(), desc);
		}

		public static void info(BufferedImage image) {
			info(image, null);
		}

		public static void info(TiledImage image) {
			info(image, null);
		}

		public static void info(List<Vector2d> lines, int width, int height,
				String desc) {
			BufferedImage img = getImage(null, lines, null, width, height, null);
			info(img, desc);
		}

		private static String getMethodName() {
			StackTraceElement[] traceElement = new Exception().getStackTrace();
			StackTraceElement elem = null;
			for (StackTraceElement e : traceElement) {
				if (!e.getClassName().equals(Image.class.getName())) {
					elem = e;
					break;
				}
			}
			String className;
			if (shortClass) {
				String name = elem.getClassName();
				className = name.substring(name.lastIndexOf(".") + 1);
			} else {
				className = elem.getClassName();
			}

			return className + "-" + elem.getMethodName() + "("
					+ elem.getLineNumber() + ")";
		}

		public void setShortClass(boolean shortClass) {
			Image.shortClass = shortClass;
		}

		public void setDescFilename(boolean descFilename) {
			Image.descFilename = descFilename;
		}

		public static void setShow(boolean show) {
			Image.show = show;
		}

		public static void setSave(boolean save) {
			Image.save = save;
		}

		public static void setDebugFolder(String debugFolder) {
			Image.debugFolder.set(new File(debugFolder));
		}

		public static void setEnabled(boolean enabled) {
			Image.enabled = enabled;
		}

		public static boolean isEnabled() {
			return enabled;
		}
	}
}
